﻿using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Controls;
using System.Windows.Input;
using System.Threading;

using SBPweb.PROvibe.Shared.Contracts.Content;

using SBPweb.PROvibe.Client.Framework.Contracts.Content;
using SBPweb.PROvibe.Client.Framework.Utilities.Events;
using SBPweb.PROvibe.Client.Framework.Utilities.Animations;

namespace SBPweb.PROvibe.Client.Framework.Controls
{

    public partial class SmartpartContainer : ContentControl, ISmartpartContainer
    {

        #region Private fields

        private IPage parentPage;
        private ISmartpart contentSp;
        private IContentManager contentManager;
        private Grid gridMain;
        private TextBlock tbTitle;
        private Grid gridBody;
        private ContentControl ccContent;
        private Button bClose;
        private Border boContent;
        private Rectangle rContainer;
        private Rectangle rContainerHover;
        private bool isDragDropInEffect = false;
        private bool isClosable = true;
        private bool isBorderless = false;
        private bool isShown = false;
        private bool isShowStarted = false;
        private bool isHideStarted = false;
        private Point pos;
        private Cursor prevCursor;

        #endregion

        #region Constructor logic

        static SmartpartContainer()
        {
            StoryboardShowProperty = DependencyProperty.Register("StoryboardShow", typeof(string), typeof(SmartpartContainer), null);
            StoryboardHideProperty = DependencyProperty.Register("StoryboardHide", typeof(string), typeof(SmartpartContainer), null);
        }

        public SmartpartContainer()
        {
            DefaultStyleKey = typeof(SmartpartContainer);
            this.Style = Application.Current.Resources["NormalContainer"] as Style;
            SizeChanged += new SizeChangedEventHandler(Container_SizeChanged);
            Closing += new EventHandler<CancelEventArgs>(Container_Closing);
            ContentChanged += new EventHandler(Container_ContentChanged);
        }

        #endregion

        #region Dependency properties

        public static readonly DependencyProperty StoryboardShowProperty;
        
        public static readonly DependencyProperty StoryboardHideProperty;

        #endregion

        #region Property accessors

        public int? SmartpartId
        {
            get;
            set;
        }

        public string SmartpartType
        {
            get;
            set;
        }

        public string Caption
        {
            get;
            set;
        }

        public bool IsClosable
        {
            get
            {
                return isClosable;
            }
            set
            {
                isClosable = value;
            }
        }

        public bool IsBorderless
        {
            get
            {
                return isBorderless;
            }
            set
            {
                isBorderless = value;
            }
        }

        public IContentManager ContentManager
        {
            get
            {
                return contentManager;
            }
            set
            {
                contentManager = value;
            }
        }

        public IPage ParentPage
        {
            get
            {
                return parentPage;
            }
            set
            {
                parentPage = value;
            }
        }

        public string StoryboardShow
        {
            get
            {
                return GetValue(SmartpartContainer.StoryboardShowProperty) as String;
            }
            set
            {
                SetValue(SmartpartContainer.StoryboardShowProperty, value);
            }
        }

        public string StoryboardHide
        {
            get
            {
                return GetValue(SmartpartContainer.StoryboardHideProperty) as String;
            }
            set
            {
                SetValue(SmartpartContainer.StoryboardHideProperty, value);
            }
        }

        public new object Content
        {
            get
            {
                if (ccContent == null)
                {
                    return base.Content;
                }
                return ccContent.Content;
            }
            set
            {
                object oldContent;
                if (ccContent == null)
                {
                    oldContent = base.Content;
                    base.Content = value;
                }
                else
                {
                    oldContent = ccContent.Content;
                    ccContent.Content = value;
                }
                OnContentChanged(oldContent, value);
            }
        }

        #endregion

        #region Events

        public event EventHandler<CancelEventArgs> Closing;

        public event EventHandler<CancelEventArgs> Showing;

        public event EventHandler<CancelEventArgs> Hiding;

        public event EventHandler<CancelEventArgs> Expanding;

        public event EventHandler<CancelEventArgs> Collapsing;

        #endregion

        #region Methods

        #region Smartpart loading

        public void LoadSmartpart()
        {
            LoadSmartpart(ContentManager, ParentPage, SmartpartId, SmartpartType);
        }

        public void LoadSmartpart(IContentManager contentManager, IPage parentPage)
        {
            LoadSmartpart(contentManager, parentPage, SmartpartId, SmartpartType);
        }

        public void LoadSmartpart(IContentManager contentManager, IPage parentPage, int smartpartId)
        {
            LoadSmartpart(contentManager, parentPage, smartpartId, null);
        }

        public void LoadSmartpart(IContentManager contentManager, IPage parentPage, string smartpartType)
        {
            LoadSmartpart(contentManager, parentPage, null, smartpartType);
        }

        public void LoadSmartpart(IContentManager contentManager, IPage parentPage, int? smartpartId, string smartpartType)
        {
            this.ContentManager = contentManager;
            this.ParentPage = parentPage;
            this.SmartpartId = smartpartId;
            this.SmartpartType = smartpartType;
            if (smartpartId.HasValue)
            {
                contentManager.LoadSmartpart(smartpartId.Value, new EventHandler<TypedObjectEventArgs<ISmartpart>>(OnSmartpartInstanceLoaded), parentPage);
            }
            else if (!string.IsNullOrEmpty(smartpartType))
            {
                contentManager.LoadSmartpart(new TypeDescriptor(smartpartType), new EventHandler<TypedObjectEventArgs<ISmartpart>>(OnSmartpartInstanceLoaded), parentPage);
            }
            else
            {
                throw new ArgumentNullException("Type could not be created!");
            }
        }

        public void LoadCustomElement(params object[] constructorParameters)
        {
            LoadCustomElement<UIElement>(constructorParameters);
        }

        public void LoadCustomElement<TElement>(params object[] constructorParameters)
            where TElement : UIElement
        {
            LoadCustomElement<TElement>(contentManager, constructorParameters);
        }

        public void LoadCustomElement(IContentManager contentManager, params object[] constructorParameters)
        {
            LoadCustomElement<UIElement>(contentManager, constructorParameters);
        }

        public void LoadCustomElement<TElement>(IContentManager contentManager, params object[] constructorParameters)
            where TElement : UIElement
        {
            LoadCustomElement<TElement>(contentManager, SmartpartType, constructorParameters);
        }

        public void LoadCustomElement(IContentManager contentManager, string smartpartType, params object[] constructorParameters)
        {
            LoadCustomElement<UIElement>(contentManager, smartpartType, constructorParameters);
        }

        public void LoadCustomElement<TElement>(IContentManager contentManager, string smartpartType, params object[] constructorParameters)
            where TElement : UIElement
        {
            this.ContentManager = contentManager;
            this.ParentPage = parentPage;
            this.SmartpartType = smartpartType;
            if (!string.IsNullOrEmpty(smartpartType))
            {
                contentManager.LoadElement<UIElement>(new TypeDescriptor(smartpartType), new EventHandler<TypedObjectEventArgs<UIElement>>(OnCustomElementInstanceLoaded), constructorParameters);
            }
            else
            {
                throw new ArgumentNullException("Type could not be created!");
            }
        }

        private void OnSmartpartInstanceLoaded(object sender, TypedObjectEventArgs<ISmartpart> e)
        {
            contentSp = e.Object;
            (contentSp as SmartpartControl).Container = this;
            UIElement uie = contentSp as UIElement;
            if (tbTitle != null)
            {
                tbTitle.Text = string.Format(tbTitle.Text, contentSp.Caption);
            }
            if (ccContent != null)
            {
                object oldValue = ccContent.Content;
                ccContent.Content = uie;
                OnContentChanged(oldValue, uie);
            }
            BeginShowStoryBoard();
        }

        private void OnCustomElementInstanceLoaded(object sender, TypedObjectEventArgs<UIElement> e)
        {
            if (ccContent != null)
            {
                object oldValue = ccContent.Content;
                ccContent.Content = e.Object;
                OnContentChanged(oldValue, e.Object);
            }
            BeginShowStoryBoard();
        }

        #endregion
        
        #region Template binding
        
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            gridMain = GetTemplateChild("SmartpartContainerGrid") as Grid;
            gridBody = GetTemplateChild("SmartpartContainerBody") as Grid;
            tbTitle = GetTemplateChild("SmartpartContainerTBTitle") as TextBlock;
            bClose = GetTemplateChild("SmartpartContainerBClose") as Button;
            ccContent = GetTemplateChild("SmartpartContainerContentControl") as ContentControl;
            boContent = GetTemplateChild("SmartpartContainerBorder") as Border;
            rContainer = GetTemplateChild("ContainerR") as Rectangle;
            rContainerHover = GetTemplateChild("ContainerRHover") as Rectangle;
            if (this.Parent is Canvas)
            {
                if (isBorderless)
                {
                    ccContent.MouseMove += new MouseEventHandler(Container_MouseMove);
                    ccContent.MouseLeftButtonDown += new MouseButtonEventHandler(Container_MouseLeftButtonDown);
                    ccContent.MouseLeftButtonUp += new MouseButtonEventHandler(Container_MouseLeftButtonUp);
                    boContent.BorderThickness = new Thickness(0);
                    rContainer.Visibility = Visibility.Collapsed;
                    rContainerHover.Visibility = Visibility.Collapsed;
                    bClose.Visibility = Visibility.Collapsed;
                    tbTitle.Visibility = Visibility.Collapsed;
                }
                else
                {
                    tbTitle.MouseMove += new MouseEventHandler(Container_MouseMove);
                    tbTitle.MouseLeftButtonDown += new MouseButtonEventHandler(Container_MouseLeftButtonDown);
                    tbTitle.MouseLeftButtonUp += new MouseButtonEventHandler(Container_MouseLeftButtonUp);
                }
                bClose.Click += new RoutedEventHandler(Container_Close_Click);
            }
            if (tbTitle != null && Caption != null)
            {
                tbTitle.Text = string.Format(tbTitle.Text, Caption);
            }
            if (gridMain != null && this.Content != null)
            {
                BeginShowStoryBoard();
            }
            if (bClose != null)
            {
                bClose.IsEnabled = IsClosable;
            }
        }

        private void Container_ContentChanged(object sender, EventArgs e)
        {
            BeginShowStoryBoard();
        }

        #endregion

        #region Animations

        public void Show()
        {
            BeginShowStoryBoard();
        }

        public void Hide()
        {
            BeginHideStoryBoard();
        }

        private void BeginShowStoryBoard()
        {
            lock (this)
            {
                if (isShowStarted || isShown)
                {
                    return;
                }
                if (gridMain == null)
                {
                    return;
                }
                this.Visibility = Visibility.Visible;
                isShowStarted = true;
                StoryboardHelper.BeginElementStoryBoard(gridMain, StoryboardShow, new PropertyPath("Opacity"), new EventHandler(Container_ShowStoryboardCompeted));
            }
        }

        private void BeginHideStoryBoard()
        {
            lock (this)
            {
                if (isHideStarted || !isShown)
                {
                    return;
                }
                isHideStarted = true;
                StoryboardHelper.BeginElementStoryBoard(gridMain, StoryboardHide, new PropertyPath("Opacity"), new EventHandler(Container_HideStoryboardCompeted));
            }
        }

        #endregion

        #region UI event handlers

        private void Container_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragDropInEffect)
            {
                FrameworkElement ele = sender as FrameworkElement;
                // Retrieving the item's current x and y position
                double xPos = e.GetPosition(null).X - pos.X;
                double yPos = e.GetPosition(null).Y - pos.Y;

                // Re-position Element
                this.SetValue(Canvas.TopProperty, yPos + (double)this.GetValue(Canvas.TopProperty));
                this.SetValue(Canvas.LeftProperty, xPos + (double)this.GetValue(Canvas.LeftProperty));

                // Reset the new position value
                pos = e.GetPosition(null);
            }
        }
        
        private void Container_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement ele = sender as FrameworkElement;
            isDragDropInEffect = true;
            // x and y coords of mouse pointer position
            pos = e.GetPosition(null);
            // Enable mouse capture on element
            ele.CaptureMouse();
            // Set the cursor to 'Hand' when mouse pointer is over element
            prevCursor = ele.Cursor;
            ele.Cursor = Cursors.Stylus;
        }

        private void Container_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (isDragDropInEffect)
            {
                FrameworkElement ele = sender as FrameworkElement;
                isDragDropInEffect = false;
                // Removes Mouse Capture from Element being dragged
                ele.ReleaseMouseCapture();
                ele.Cursor = prevCursor;
            }
        }

        private void Container_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Canvas parentCanvas = this.Parent as Canvas;
            if (parentCanvas != null)
            {
                if (parentCanvas.HorizontalAlignment == HorizontalAlignment.Center)
                {
                    this.SetValue(Canvas.LeftProperty, e.NewSize.Width / -2);
                }
                if (parentCanvas.VerticalAlignment == VerticalAlignment.Center)
                {
                    this.SetValue(Canvas.TopProperty, e.NewSize.Height / -2);
                }
            }
        }

        private void Container_Close_Click(object sender, RoutedEventArgs e)
        {
            if (Closing != null)
            {
                CancelEventArgs cea;
                Closing(this, cea = new CancelEventArgs());
                if (cea.Cancel)
                {
                    return;
                }
            }
            BeginHideStoryBoard();
        }

        private void Container_ShowStoryboardCompeted(object sender, EventArgs e)
        {
            lock (this)
            {
                isShowStarted = false;
                isShown = true;
            }
        }

        private void Container_HideStoryboardCompeted(object sender, EventArgs e)
        {
            lock (this)
            {
                isHideStarted = false;
                isShown = false;
                this.Visibility = Visibility.Collapsed;
            }
        }

        private void Container_Closing(object sender, CancelEventArgs e)
        {
            e.Cancel = !IsClosable;
        }

        #endregion

        #endregion

    }

}